package com.cloud.poetry.base.file;

import cn.hutool.core.util.StrUtil;
import com.cloud.poetry.core.exception.FileException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.*;

import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.util.*;

/**
 * @author mengxj
 */
public final class ExcelReader {

    private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#");

    private Workbook wb;

    private Sheet sheet;

    private boolean sameCol;

    private Map<String, String> rowData = new HashMap<>();

    private List<String> title = new ArrayList<>();

    private ExcelRow excelData;

    private ExcelReader(){}

    public static ExcelReader getInstance(ExcelRow excelData){
        ExcelReader reader = new ExcelReader();
        reader.sameCol = true;
        reader.excelData = excelData;
        return reader;
    }

    public void read(InputStream in) throws IOException, InvalidFormatException {
        this.read(in, this.sameCol);
    }

    public void read(InputStream in, boolean sameCol) throws IOException, InvalidFormatException {
        this.read(in, -1, sameCol);
    }

    public void read(InputStream in, int sheetIndex, boolean sameCol) throws IOException, InvalidFormatException {
        this.sameCol = sameCol;
        this.wb = WorkbookFactory.create(in);
        if(-1 == sheetIndex){
            int sheets = wb.getNumberOfSheets();
            for(int i = 0; i < sheets; i ++){
                this.read1(i);
            }
        }else{
            this.read1(sheetIndex);
        }
    }

    private void read1(int sheetIndex){
        this.sheet = wb.getSheetAt(sheetIndex);
        this.readSheet();
        this.title = new ArrayList<>(this.title.size());
    }

    private void readSheet(){
        int firstRow = this.sheet.getFirstRowNum(), lastRow = this.sheet.getLastRowNum();
        Row row = this.sheet.getRow(firstRow);
        short first = row.getFirstCellNum(), last = row.getLastCellNum();
        for(short i = first; i <= last; i ++){
            Cell cell = row.getCell(i);
            this.title.add(this.getCellValue(cell));
        }
        if(!sameCol){
            int totalSize = this.title.size();
            int distinctSize = (int) this.title.stream().distinct().count();
            if(totalSize != distinctSize){
                throw new FileException("导入失败：检测到有相同的列！");
            }
        }
        for(int i = firstRow + 1; i <= lastRow; i ++){
            row = this.sheet.getRow(i);
            for(short j = first; j <= last; j ++){
                Cell cell = row.getCell(j);
                rowData.put(this.title.get(j), this.getCellValue(cell));
            }
            this.excelData.rowData(this.rowData, this.title, this.sheet.getSheetName());
        }
    }

    private String getCellValue(Cell cell) {
        if (Objects.isNull(cell)) {
            return "";
        }
        String line;
        CellType type = cell.getCellTypeEnum();
        switch (type) {
            case BOOLEAN:
                line = String.valueOf(cell.getBooleanCellValue());
                break;
            case ERROR:
                line = String.valueOf(cell.getErrorCellValue());
                break;
            case NUMERIC:
                line = String.valueOf(cell.getNumericCellValue());
                if(StrUtil.isBlank(line)){
                    return "";
                }
                line = DECIMAL_FORMAT.format(cell.getNumericCellValue());
                break;
            case STRING:
                line = cell.getStringCellValue();
                break;
            default:
                line = "";
        }
        return line;
    }

    interface ExcelRow {

        /**
         * 处理数据方法，具体由使用者实现
         * @param rowData 一行数据
         * @param title 表头
         * @param sheetName sheet页名称
         */
        void rowData(Map<String, String> rowData, List<String> title, String sheetName);
    }
}
